<template>
  <el-container>
    <!-- user list, 需要显示 id, 状态(是否在分享xx) -->
    <el-aside>
      <div class="user-table-container">
        <el-table
          :data="users"
          @current-change="handle_select"
          :height="max_height"
        >
          <el-table-column prop="name" label="昵称"> </el-table-column>
          <el-table-column prop="id" label="id"> </el-table-column>
          <el-table-column prop="state" label="状态"> </el-table-column>
        </el-table>
        <!--
        <div style="height=30px">
          <el-button @click="update_table">update</el-button>
        </div>
        -->
      </div>
    </el-aside>
    <el-main style="padding: 10px">
      <!-- video table, 弄一个容器, 会根据子部件的数量自动布局? -->
      <div ref="video_container" class="video-container">
        <video
          controls
          muted
          autoplay
          :width="video_width"
          ref="local_video"
          v-show="local_stream !== null"
        ></video>
        <video
          controls
          muted
          autoplay
          v-for="([user_id, connection], _) in recv_connections"
          :key="user_id"
          :width="video_width"
          :ref="
            (video) => {
              if (video) videos.set(user_id, video);
            }
          "
        ></video>
      </div>
      <div ref="meeting_buttons" class="meeting-buttons">
        <el-button @click="turn_on_video">开启视频</el-button>
        <el-button @click="share_screen">共享屏幕</el-button>
        <el-button @click="mute">静音</el-button>
        <el-button @click="close_share">关闭分享</el-button>
        <span
          >---- id: {{ self_id }}, name: {{ self_name }}, room_id:
          {{ room_id }}</span
        >
      </div>
      <!--
      -->
    </el-main>
  </el-container>
</template>

<script lang="ts">
import {
  defineComponent,
  onMounted,
  ref,
  Ref,
  watchEffect,
  nextTick,
} from "vue";
import router from "../router";
import {
  is_object,
  json_type,
  MeetingProps,
  MeetingMsg,
  Forward,
  MeetingMsgNewIn,
  MeetingMsgYourId,
  MeetingMsgOther,
  MeetingMsgDisconnection,
  NoRoom,
  WrongPassword,
  NeedPassword,
  WrongPasswordTimesOverLimit,
  MeetingMsgNewRtcPeerConnection,
  MeetingMsgOffer,
  MeetingMsgAnswer,
  MeetingMsgIceCandidate,
  to_other,
  broadcast,
  MeetingMsgDisconnectPeer,
  MeetingMsgOneExit,
} from "../utils/types";
import { ROOM_WS_URL, WEBRTC_CONFIG } from "../utils/constant";

enum State {
  Watcher,
  BroadCaster,
}

type User = {
  id: number;
  name: string;
  state: State;
};

// TODO: 选择分享的 button
// MAYBE: 聊天功能
export default defineComponent<MeetingProps>({
  setup: (props) => {
    // 准备阶段, 初始化一些东西
    // 与设备有关的
    const video_container = ref(null);
    const max_height = document.body.clientHeight - 60;

    // room_id
    const cur_paths = router.currentRoute.value.path.split("/");
    const room_id = parseInt(cur_paths[cur_paths.length - 1]);

    // 与视频会议有关的
    // FIXME: id有必要 ref 么,
    const self_name = ref("defalut");
    const self_id: Ref<null | number> = ref(null);
    let local_stream: Ref<null | MediaStream> = ref(null);
    const users: Ref<User[]> = ref([]);
    const videos: Ref<Map<number, HTMLVideoElement>> = ref(new Map());
    const local_video: Ref<null | HTMLVideoElement> = ref(null);

    const send_connections: Ref<Map<number, RTCPeerConnection>> = ref(
      new Map()
    );
    const recv_connections: Ref<Map<number, RTCPeerConnection>> = ref(
      new Map()
    );

    // help function
    function on_ice_candidate(
      candidate: RTCIceCandidate | null,
      to: number,
      tag: "recv" | "send"
    ) {
      if (candidate === null) return;
      let ice_msg: MeetingMsgIceCandidate = {
        ice_candidate: {
          tag,
          user_id: self_id.value!,
          data: candidate,
        },
      };
      socket.send(JSON.stringify(to_other(to, ice_msg)));
    }

    // websocket build
    const socket = new WebSocket(`${ROOM_WS_URL}/${room_id}`);
    socket.onopen = () => {
      console.log("websocket open");
    };
    socket.onclose = () => {
      let msg: MeetingMsgDisconnection = {
        disconnection: { user_id: self_id.value! },
      };
      socket.send(JSON.stringify(msg));
    };
    // IMPROVE: 如果重构的话, 具体见 readme 中 websocket相关
    socket.onmessage = async (event) => {
      let msg = (event as any).data;
      console.log("get msg: ", msg);

      if (!is_object(msg)) {
        switch (msg) {
          case NoRoom: {
            alert("房间不存在");
            break;
          }
          case NeedPassword: {
            // TODO: 向用户申请密码, 然后发送
            break;
          }
          case WrongPassword: {
            // TODO: 同上
            break;
          }
          case WrongPasswordTimesOverLimit: {
            // TODO: 如果这玩意写的更完善的话, 可以弄个 cookie 记录下,当天不允许登陆该房间
            alert("密码错误达到上限, 请重新登陆, 或者拜拜");
            break;
          }
          default: {
            console.warn("ws 收到了错误类型的 msg: ", msg);
            break;
          }
        }
      } else {
        msg = JSON.parse(msg);
        switch (json_type(msg)) {
          // 服务器发来的 自身id
          case "YourId": {
            let obj: MeetingMsgYourId = msg;
            self_id.value = obj.YourId.id;
            let new_in_msg: MeetingMsgNewIn = {
              new_in: { user_id: self_id.value, name: self_name.value },
            };
            socket.send(JSON.stringify(broadcast(new_in_msg)));
            break;
          }
          // 有其他用户加入房间
          case "new_in": {
            let obj: MeetingMsgNewIn = msg;
            if (obj.new_in.user_id === self_id.value!) return;
            users.value.push({
              id: obj.new_in.user_id,
              name: obj.new_in.name,
              state: State.Watcher,
            });
            let to_other_msg: MeetingMsgOther = {
              other: {
                name: self_name.value,
                id: self_id.value!,
              },
            };

            if (local_stream.value !== null) {
              new_connection(local_stream.value, obj.new_in.user_id);
            }
            socket.send(
              JSON.stringify(to_other(obj.new_in.user_id, to_other_msg))
            );
            break;
          }
          // 其他客户发过来的他们的id和一些信息
          case "other": {
            let obj: MeetingMsgOther = msg;
            // 这个也可以放在服务端跳过
            if (obj.other.id === self_id.value) return;
            const new_user = {
              id: obj.other.id,
              name: obj.other.name,
              // 默认为看客, 如果建立了链接为 BroadCaster
              state: State.Watcher,
            };
            users.value.push(new_user);
            console.log(users.value);

            // TODO: 刷新 user-table
            break;
          }
          // 有其他用户退出房间
          case "disconnection": {
            let obj: MeetingMsgOther = msg;
            users.value.splice(
              users.value.findIndex((user) => user.id === obj.other.id),
              1
            );
            break;
          }
          // RTCPeerConnection 需要的一些信息
          case "offer": {
            let obj: MeetingMsgOffer = msg;

            let connection = new RTCPeerConnection(WEBRTC_CONFIG);
            connection.onicecandidate = (event) =>
              on_ice_candidate(event.candidate, obj.offer.user_id, "recv");
            connection.ontrack = (event) => {
              console.log("recv add track: " + event.streams);
              nextTick().then(() => {
                console.log(videos.value);
                videos.value.get(obj.offer.user_id)!.srcObject =
                  event.streams[0];
              });
            };

            recv_connections.value.set(obj.offer.user_id, connection);
            await connection.setRemoteDescription(obj.offer.data);
            let answer = await connection.createAnswer();
            await connection.setLocalDescription(answer);
            let answer_msg: MeetingMsgAnswer = {
              answer: {
                user_id: self_id.value!,
                // data: connection.localDescription,
                data: answer,
              },
            };

            socket.send(
              JSON.stringify(to_other(obj.offer.user_id, answer_msg))
            );
            break;
          }
          case "answer": {
            let obj: MeetingMsgAnswer = msg;
            let connection = send_connections.value.get(obj.answer.user_id)!;
            connection.setRemoteDescription(obj.answer.data);
            break;
          }
          case "ice_candidate": {
            let obj: MeetingMsgIceCandidate = msg;
            let connection;
            // TODO: 可以用 hash优化一下写法
            if (obj.ice_candidate.tag === "send") {
              connection = recv_connections.value.get(
                obj.ice_candidate.user_id
              )!;
            } else {
              connection = send_connections.value.get(
                obj.ice_candidate.user_id
              )!;
            }
            connection.addIceCandidate(obj.ice_candidate.data);
            break;
          }
          // TODO: 实现 disconnect 的具体逻辑
          case "disconnect_peer": {
            let obj: MeetingMsgDisconnectPeer = msg;
            if (obj.disconnect_peer.id === self_id.value!) {
              return;
            }
            if (obj.disconnect_peer.tag === "send") {
              recv_connections.value.get(obj.disconnect_peer.id)?.close();
              recv_connections.value.delete(obj.disconnect_peer.id);
            }
            if (obj.disconnect_peer.tag === "recv") {
              send_connections.value.get(obj.disconnect_peer.id)?.close();
              send_connections.value.delete(obj.disconnect_peer.id);
            }
            break;
          }

          case "OneExit": {
            let obj: MeetingMsgOneExit = msg;
            users.value.splice(
              users.value.findIndex((x) => x.id === obj.OneExit.id),
              1
            );
            send_connections.value.delete(obj.OneExit.id);
            recv_connections.value.delete(obj.OneExit.id);
            break;
          }
          // case "new_rtc_peer_connection": {
          //   let obj: MeetingMsgNewRtcPeerConnection = msg;
          //   connections.set(
          //     obj.new_rtc_peer_connection.id,
          //     new RTCPeerConnection(WEBRTC_CONFIG)
          //   );
          //  break;
          // }
          default: {
            console.warn("ws 收到了错误类型的 msg_object: ", msg);
          }
        }
      }
    };

    const handle_select = (selected: any) => {
      console.log(selected);
    };

    const share_screen = async () => {
      local_stream.value = await navigator.mediaDevices.getDisplayMedia();
      if (local_stream.value != null) {
        await share_stream(local_stream.value);
        open_local_video();
      } else alert("获取视频流失败");
    };
    const turn_on_video = async () => {
      local_stream.value = await navigator.mediaDevices.getUserMedia({
        audio: true,
        video: true,
      });
      if (local_stream.value != null) {
        await share_stream(local_stream.value);
        open_local_video();
      } else alert("获取视频流失败");
    };

    const open_local_video = () => {
      if (local_stream.value !== null) {
        local_video.value!.srcObject = local_stream.value;
      }
    };

    const share_stream = async (stream: MediaStream) => {
      for (const user of users.value) {
        await new_connection(stream, user.id);
      }
    };

    // send offer
    const new_connection = async (stream: MediaStream, to_id: number) => {
      let connection = new RTCPeerConnection(WEBRTC_CONFIG);

      send_connections.value.set(to_id, connection);

      connection.onicecandidate = (event) => {
        on_ice_candidate(event.candidate, to_id, "send");
      };

      stream.getTracks().forEach((track) => {
        connection.addTrack(track, stream);
      });

      let offer = await connection.createOffer();
      await connection.setLocalDescription(offer);
      let offer_msg: MeetingMsgOffer = {
        offer: {
          user_id: self_id.value!,
          // data: connection.localDescription,
          data: offer,
        },
      };
      socket.send(JSON.stringify(to_other(to_id, offer_msg)));
    };

    // TODO:
    const mute = () => {};

    // 动态的视频宽度
    const video_width = ref(0);
    watchEffect(
      () => {
        let size = Math.ceil(Math.sqrt(recv_connections.value.size));
        if (size === 0) size = 1;
        if (local_stream !== null) size += 1;
        video_width.value =
          ((video_container.value as any).clientWidth - 80) / size;
      },
      {
        flush: "post",
      }
    );

    const close_share = () => {
      local_stream.value = null;
      const msg: MeetingMsgDisconnectPeer = {
        disconnect_peer: {
          tag: "send",
          id: self_id.value!,
        },
      };
      socket.send(JSON.stringify(broadcast(msg)));
      for (const [key, conn] of send_connections.value) {
        conn.close();
      }
      send_connections.value.clear();
    };
    return {
      close_share,
      video_width,
      users,
      recv_connections,
      handle_select,
      local_stream,
      local_video,
      share_screen,
      video_container,
      max_height,
      turn_on_video,
      mute,
      videos,
      self_id,
      self_name,
      room_id,
    };
  },
});
</script>

<style>
.video-container {
  border: 1px solid #eee;
  width: 100%;
  height: 100%;
}
</style>